package com.walle;

import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Arrays;
import java.util.StringTokenizer;
import java.util.Vector;

public class ClassPath
{
  private final Vector<File> _elements = new Vector<File>();

  public ClassPath()
  {
  }

  public ClassPath(String initial)
  {
    addClasspath(initial);
  }

  public File[] getElements()
  {
    return (File[])this._elements.toArray(new File[this._elements.size()]);
  }

  public int count()
  {
    return this._elements.size();
  }

  public boolean addComponent(String component)
  {
    if ((component != null) && (component.length() > 0))
    {
      try
      {
        File f = new File(component);
        if (f.exists())
        {
          File key = f.getCanonicalFile();
          if (!this._elements.contains(key))
          {
            this._elements.add(key);
            return true;
          }
        }
      }
      catch (IOException localIOException)
      {
      }
    }
    return false;
  }

  public boolean addComponent(File component)
  {
    if (component != null)
    {
      try
      {
        if (component.exists())
        {
          File key = component.getCanonicalFile();
          if (!this._elements.contains(key))
          {
            this._elements.add(key);
            return true;
          }
        }
      }
      catch (IOException localIOException)
      {
      }
    }
    return false;
  }

  public boolean addClasspath(String s)
  {
    boolean added = false;
    if (s != null)
    {
      StringTokenizer t = new StringTokenizer(s, File.pathSeparator);
      while (t.hasMoreTokens())
      {
        added |= addComponent(t.nextToken());
      }
    }
    return added;
  }

  public void dump(PrintStream out)
  {
    int i = 0;
    for (File element : this._elements)
    {
      out.printf("%2d: %s\n", new Object[] { Integer.valueOf(i++), element.getAbsolutePath() });
    }
  }

  public String toString()
  {
    StringBuffer cp = new StringBuffer(1024);
    int cnt = this._elements.size();
    if (cnt >= 1)
    {
      cp.append(((File)this._elements.elementAt(0)).getPath());
    }
    for (int i = 1; i < cnt; i++)
    {
      cp.append(File.pathSeparatorChar);
      cp.append(((File)this._elements.elementAt(i)).getPath());
    }
    return cp.toString();
  }

  public ClassLoader getClassLoader()
  {
    int cnt = this._elements.size();
    URL[] urls = new URL[cnt];
    for (int i = 0; i < cnt; i++)
    {
      try
      {
        String u = ((File)this._elements.elementAt(i)).toURI().toURL()
          .toString();
        System.err.println( u );
        urls[i] = new URL(encodeFileURL(u));
      }
      catch (MalformedURLException e)
      {
        e.printStackTrace();
      }
    }

    ClassLoader parent = Thread.currentThread().getContextClassLoader();
    if (parent == null)
    {
      parent = ClassPath.class.getClassLoader();
      if (parent == null) {
        parent = ClassLoader.getSystemClassLoader();
      }
    }
    return new Loader(urls, parent);
  }

  public static String encodeFileURL(String path)
  {
    byte[] bytes;
    try
    {
      bytes = path.getBytes("utf-8");
    }
    catch (UnsupportedEncodingException e)
    {
      bytes = path.getBytes();
    }

    StringBuffer buf = new StringBuffer(bytes.length * 2);
    buf.append("file:");

    synchronized (buf)
    {
      for (int i = 5; i < bytes.length; i++)
      {
        byte b = bytes[i];
        switch (b)
        {
        case 37:
          buf.append("%25");
          break;
        case 32:
          buf.append("%20");
          break;
        case 45:
        case 46:
        case 47:
        case 95:
          buf.append((char)b);
          break;
        default:
          if (Character.isJavaIdentifierPart((char)b))
          {
            if (((b >= 97) && (b <= 122)) || ((b >= 65) && (b <= 90)) || (
              (b >= 48) && (b <= 57)))
            {
              buf.append((char)b);
              continue;
            }
          }
          buf.append('%');
          buf.append(Integer.toHexString((0xF0 & b) >> 4));
          buf.append(Integer.toHexString(0xF & b));
        }
      }

    }

    return buf.toString();
  }

  public void overlay(ClassPath cpOther)
  {
    for (File otherElement : cpOther._elements)
    {
      if (!this._elements.contains(otherElement))
      {
        this._elements.add(otherElement);
      }
    }
  }

  public boolean isEmpty() {
    return (this._elements == null) || (this._elements.isEmpty());
  }

  private static class Loader extends URLClassLoader
  {
    String name;

    Loader(URL[] urls, ClassLoader parent)
    {
      super(urls);
      this.name = ("StartLoader" + Arrays.asList(urls));
    }

    public String toString()
    {
      return this.name;
    }
  }
}
